package com.stu.design_patterns.proxy._02_dynamic_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 我们发现测试类中每次都要手动调用 Proxy.newProxyInstance 方法，很麻烦。尝试简化：
public class Client2 {
  public static void main(String[] args) {
    // test();
    // test1();
    // test2();
    test3();
  }

  // 测试普通版：
  public static void test() {
    Marry marry = new Newcomer();
    MarryHandler handler = new MarryHandler(marry);
    // Marry marryProxy = (Marry)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), marry.getClass().getInterfaces(), handler);
    Marry marryProxy = (Marry)Proxy.newProxyInstance(marry.getClass().getClassLoader(), marry.getClass().getInterfaces(), handler);
    marryProxy.happyMarry();
    marryProxy.test();
  }

  // 测试简化版本1：
  public static void test1() {
    Marry marryProxy = (Marry)new DynamicProxy().getProxyObject1(new Newcomer());
    marryProxy.happyMarry();
  }

  // 测试简化版本2：
  public static void test2() {
    try {
      // Newcomer marryProxy = new DynamicProxy().getProxyObject2(Newcomer.class);
      Marry marryProxy = new DynamicProxy().getProxyObject2(Newcomer.class);
      marryProxy.happyMarry();
      // Marry marryDog = new DynamicProxy().getProxyObject2(Dog.class);
      // marryDog.happyMarry();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  // 测试简化版本3：
  public static void test3() {
    try {
      Marry marryProxy = new DynamicProxy().getProxyObject3(Newcomer.class, null);
      marryProxy.happyMarry();
      Marry marryDog = new DynamicProxy().getProxyObject3(Dog.class, new Class[]{String.class}, "八公");
      marryDog.happyMarry();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}



class DynamicProxy implements InvocationHandler {
  private Object target;
  // public DynamicProxy(Object target) { this.target = target; }

  /**
   * 简化版本1：
   *   改动1：把对 target 的赋值从构造器中移到了这里；
   *   改动2：把对 Proxy.newProxyInstance 的调用从客户端移动到了这里
   *   缺点：代理类实例必须进行强制类型转换
   * @param target 真实角色实例
   * @return 代理角色实例
   */
  public Object getProxyObject1(Object target) {
    this.target = target;
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
  }

  /**
   * 简化版本2：
   *   改动：把参数从真实类实例 target 改为真实类的 Class 对象 targetClass；
   *   解决：解决了“代理类实例必须进行强制类型转换”的问题；
   *   缺点1：在客户端调用时，变量类型必须是接口类型，而不能是真实类型，否则报类型转换异常；
   *   缺点2：传递的类必须具有无参构造，否则报 NoSuchMethodException
   * @param <T> 抽象角色（业务接口）
   * @param targetClass 真实角色的类对象
   * @return 代理角色实例
   * @throws Exception
   */
  @SuppressWarnings("unchecked")
  public <T> T getProxyObject2(Class<T> targetClass) throws Exception {
    this.target = targetClass.newInstance();
    return (T)Proxy.newProxyInstance(targetClass.getClassLoader(), targetClass.getInterfaces(), this);
  }

  // 简化版本3：缺点是必须传递构造器的参数类型（parameterTypes 参数）
  /**
   * 简化版本3：针对简化版本2的缺点2进行了改动
   *   缺点：方法调用变得更加复杂了；
   * @param <T> 抽象角色（业务接口）
   * @param targetClass 真实角色的类对象
   * @param parameterTypes 构造器的参数类型
   * @param initargs 构造器的参数值
   * @return 代理角色实例
   * @throws Exception
   */
  @SuppressWarnings("unchecked")
  public <T> T getProxyObject3(Class<T> targetClass, Class<?>[] parameterTypes, Object ... initargs) throws Exception {
    this.target = targetClass.getDeclaredConstructor(parameterTypes).newInstance(initargs);
    return (T)Proxy.newProxyInstance(targetClass.getClassLoader(), targetClass.getInterfaces(), this);
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("前置任务");
    Object returnValue = method.invoke(target, args);
    System.out.println("后置任务");
    return returnValue;
  }
}